import backtrader as bt
import backtrader.indicators as bi
from tools import backtest
import pandas as pd
import numpy as np
import math
import matplotlib.pyplot as plt
import pymssql
from datetime import datetime
from urllib.parse import quote_plus as urlquote
from configparser import ConfigParser
import akshare as ak

pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)

conf = ConfigParser()
conf.read('env.ini')

sqlserver = ('sqlserver_out', 'sqlserver_in')[0]

host = conf.get(sqlserver, 'host')
port = conf.get(sqlserver, 'port')
user = conf.get(sqlserver, 'user')
password = conf.get(sqlserver, 'password')
schema = conf.get(sqlserver, 'schema')
db_name = conf.get(sqlserver, 'db')


class GridStrategy(bt.Strategy):
    params = (
        ("printlog", True),
        ("top", 4.2),
        ("buttom", 3.5),
    )

    def __init__(self):
        self.order = None
        self.mid = (self.p.top + self.p.buttom) / 2.0
        # 百分比区间计算
        # 这里多1/2，是因为arange函数是左闭右开区间。
        perc_level = [x for x in np.arange(1 + 0.02 * 5, 1 - 0.02 * 5 - 0.02 / 2, -0.02)]
        # 价格区间
        # print(self.mid)
        self.price_levels = [self.mid * x for x in perc_level]
        print(self.price_levels)
        # 记录上一次穿越的网格
        self.last_price_index = None
        # 总手续费
        self.comm = 0.0

    def next(self):
        if self.order:  # 查看是否有指令执行，如果有则不执行这bar
            return

        # 开仓
        if self.last_price_index is None:
            for i in range(len(self.price_levels)):
                if self.data.close[0] > self.price_levels[i]:
                    self.last_price_index = i
                    print(f'开仓->datetime->{self.data.datetime.datetime(0)}->close={self.data.close[0]}->idx={self.last_price_index}->per={i / (len(self.price_levels) - 1)}')
                    self.order = self.order_target_percent(target=i / (len(self.price_levels) - 1))
                    return
        # 调仓
        else:
            signal = False
            while True:
                upper = None
                lower = None
                if self.last_price_index > 0:
                    upper = self.price_levels[self.last_price_index - 1]
                if self.last_price_index < len(self.price_levels) - 1:
                    lower = self.price_levels[self.last_price_index + 1]
                # 还不是最轻仓，继续涨，再卖一档
                if upper is not None and self.data.close > upper:
                    self.last_price_index = self.last_price_index - 1
                    signal = True
                    continue
                # 还不是最重仓，继续跌，再买一档
                if lower is not None and self.data.close < lower:
                    self.last_price_index = self.last_price_index + 1
                    signal = True
                    continue
                break
            if signal:
                # self.long_short = None
                print(f'signal->datetime->{self.data.datetime.datetime(0)}->close={self.data.close[0]}->idx={self.last_price_index}->per={self.last_price_index / (len(self.price_levels) - 1)}')
                self.order = self.order_target_percent(target=self.last_price_index / (len(self.price_levels) - 1))

    # 输出交易记录
    def log(self, txt, dt=None, doprint=True):
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        # 未被处理的订单
        if order.status in [order.Submitted, order.Accepted]:
            return
        # 已经处理的订单
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            code = order.data._name[:6]
            if order.isbuy():
                self.log(
                    f'code={code} BUY CREATE TIME: {bt.num2date(order.created.dt)}, EXECUTED TIME: {bt.num2date(order.executed.dt)}')
                self.log(
                    'BUY EXECUTED, ref:%.0f，Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %
                    (order.ref,  # 订单编号
                     order.executed.price,  # 成交价
                     order.executed.value,  # 成交额
                     order.executed.comm,  # 佣金
                     order.executed.size,  # 成交量
                     code))  # 股票名称
                # trade_dt = int(bt.num2date(order.created.dt).strftime('%Y%m%d%H%M%S'))
                # trade_item = [trade_dt, code, 'BUY', round(order.executed.price, 2), order.executed.size,
                #               round(order.executed.comm, 2)]
                # self.buy_info[code]['cost'] = order.executed.price
                # self.trade_info.append(trade_item)
            else:  # Sell
                self.log(
                    f'code={code} SELL CREATE TIME: {bt.num2date(order.created.dt)}, EXECUTED TIME: {bt.num2date(order.executed.dt)}')
                self.log('SELL EXECUTED, ref:%.0f, Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %
                         (order.ref,
                          order.executed.price,
                          order.executed.value,
                          order.executed.comm,
                          order.executed.size,
                          code))
                # trade_dt = int(bt.num2date(order.created.dt).strftime('%Y%m%d%H%M%S'))
                # trade_item = [trade_dt, code, 'SELL', round(order.executed.price, 2),
                #               order.executed.size, round(order.executed.comm, 2)]
                # self.trade_info.append(trade_item)
            self.bar_executed = len(self)

        # Write down: no pending order
        self.order = None

    # 输出手续费
    def stop(self):
        self.log("手续费:%.2f 成本比例:%.5f" % (self.comm, self.comm / self.broker.getvalue()))


def get_data(code, st, et):
    df = ak.fund_etf_hist_em(symbol=code, period="daily", start_date=st, end_date=et, adjust="qfq")
    df.rename(columns={'日期': 'date', '开盘': 'open', '收盘': 'close', '最高': 'high', '最低': 'low', '成交量': 'volume'}, inplace=True)
    df['openinterest'] = 0
    df = df[['date', 'open', 'high', 'low', 'close', 'volume', 'openinterest']]
    # df.date = pd.to_datetime(df.date, format='%Y-%m-%d', utc=True)
    # df.set_index('date', inplace=True)
    df.index = pd.to_datetime(df.date)
    df.sort_index(inplace=True)

    return df


def exec_sql(sql):
    conn = pymssql.connect(host=host, port=port, user=user, password=password, database=db_name)
    cursor = conn.cursor()
    cursor.execute(sql)
    r = cursor.fetchall()
    cursor.close()
    conn.close()
    return r


def time_map(x):
    t = datetime.strptime(str(x), '%Y%m%d%H%M')
    return t


pre_ratio = 0.0048
ratio = 0.1
trail_ratio = 0.035
count_info = dict()
# stocks = get_stocks()
init_cash = 1e5


def run_backtrade(stocks):
    cerebro = bt.Cerebro()
    st = '20230101'
    et = '20230718'

    for code in stocks:
        data = get_data(code, st, et)
        datafeed = bt.feeds.PandasData(dataname=data)
        cerebro.adddata(datafeed, name=f'{code}')

        print(f'{code} feeds ok')

    ##   st_date = datetime.datetime(2023, 3, 1)
    ##ed_date = datetime.datetime(2023, 6, 5)

    # 初始资金 100,000,0
    cerebro.broker.setcash(init_cash)
    # 佣金，双边各 0.0003
    cerebro.broker.setcommission(commission=0.0003)
    # 滑点：双边各 0.0001
    cerebro.broker.set_slippage_perc(perc=0.0001)

    cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='pnl')  # 返回收益率时序数据
    cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn')  # 年化收益率
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='_SharpeRatio')  # 夏普比率
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown')  # 回撤

    cerebro.addstrategy(GridStrategy)

    result = cerebro.run()
    strat = result[0]
    # 返回日度收益率序列
    daily_return = pd.Series(strat.analyzers.pnl.get_analysis())
    # 打印评价指标
    print("--------------- TimeReturn -----------------")
    print(daily_return)
    print("--------------- AnnualReturn -----------------")
    print(strat.analyzers._AnnualReturn.get_analysis())
    print("--------------- SharpeRatio -----------------")
    print(strat.analyzers._SharpeRatio.get_analysis())
    print("--------------- DrawDown -----------------")
    print(strat.analyzers._DrawDown.get_analysis())

    cerebro.plot()


def main():
    stocks = ['510300']
    run_backtrade(stocks)


if __name__ == "__main__":
    main()
